﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Devices.Bluetooth.GenericAttributeProfile;
using Windows.Storage.Streams;
namespace Bluno.Core
{
    public class BlunoDevice
    {
        GattCharacteristic commandService, serialService;
        const ushort COMMAND_SERVICE_ID = 0xDFB2;
        const ushort SERIAL_SERVICE_ID = 0xDFB1;
        public ISerialHandler SerialHandler { get; set; }
        public event EventHandler<bool> ConnectionStatusChanged;
        private GattDeviceService serviceSource;
        private BlunoDevice(GattDeviceService service )
        {
            DeviceID = service.DeviceId;
            UUID = service.Uuid;
            commandService = service.GetCharacteristics(GattCharacteristic.ConvertShortIdToUuid(COMMAND_SERVICE_ID))[0];
            serialService = service.GetCharacteristics(GattCharacteristic.ConvertShortIdToUuid(SERIAL_SERVICE_ID))[0];
            
            IsConnected = false;
            serviceSource = service;
        }

        public string DeviceID { get; set; }
        public Guid UUID { get; set; }

        public void Connect()
        {
            //BUG: unable to send command, need fix in future version
            //await sendCommand(commandService, "AT+PASSWORD=DFRobot\r\n");
            //await sendCommand(commandService, "AT+CURRUART=115200\r\n");
            
            serialService.ValueChanged += serialService_ValueChanged;
            IsConnected = true;
            if (ConnectionStatusChanged!=null)
            {
                ConnectionStatusChanged(this, true);
            }
        }

        // As there's no REAL disconnect method for BLE device on windows, we just disable the event handler 
        public void Disconnect()
        {
            serialService.ValueChanged -= serialService_ValueChanged;
            IsConnected = false;
            if (ConnectionStatusChanged != null)
            {
                ConnectionStatusChanged(this, false);
            }
        }

        public bool IsConnected { get; private set; }

        private async Task sendCommand(GattCharacteristic c, string content)
        {
            //serialService = serviceSource.GetCharacteristics(GattCharacteristic.ConvertShortIdToUuid(SERIAL_SERVICE_ID))[0];
            Windows.Storage.Streams.InMemoryRandomAccessStream im = new Windows.Storage.Streams.InMemoryRandomAccessStream();
            Windows.Storage.Streams.DataWriter writer = new Windows.Storage.Streams.DataWriter(im);
            writer.WriteString(content);
            var y = writer.DetachBuffer();
            var x = await c.WriteValueAsync(y,GattWriteOption.WriteWithoutResponse);
            if (x==GattCommunicationStatus.Unreachable)
            {
                throw new InvalidOperationException("Device unreachable");
            }
        }

        public async void Send(string data)
        {
            await sendCommand(serialService, data);
        }

        public async void Send(PlanProtocol data)
        {
            await sendCommand(serialService, data.ToString());
        }

        private async void serialService_ValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args)
        {
            if (SerialHandler!=null)
            {
                using(DataReader reader=DataReader.FromBuffer(args.CharacteristicValue))
	            {
                    string s=reader.ReadString(args.CharacteristicValue.Length);
                    try
                    {
                        await SerialHandler.ProcessData(s, args.Timestamp);
                    }
                    catch (Exception ex)
                    {
                        //TODO:log error
                    }
                    
	            }
            }
        }


        public static async Task<List<BlunoDevice>> ScanBlunoDevices()
        {
            List<BlunoDevice> result = new List<BlunoDevice>();
            var devices = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(GattDeviceService.GetDeviceSelectorFromShortId(0xDFB0));
            if (devices.Count == 0)
                return null;
            //Connect to the service  
            System.Diagnostics.Debug.WriteLine(devices[0].Id);

            foreach (var item in devices)
            {
                result.Add(new BlunoDevice(await GattDeviceService.FromIdAsync(item.Id)));
            }
            return result;
        }
    }
}
